package dml.users.proc;

import dml.common.id.aggr.IdGenerator;
import dml.users.aggr.*;
import dml.users.repo.*;

public class ThirdPartyloginProcesses {

    private UuidUserBindFactoryRepository<UuidUserBindFactory<UuidUserBind, UuidUserBindProperties>> uuidUserBindFactoryRepository;

    private UuidUserBindRepository<UuidUserBind> uuidUserBindRepository;

    private UserFactoryRepository<UserFactory<User, UserProperties>> userFactoryRepository;

    private UserIdGeneratorRepository<IdGenerator<Object>> userIdGeneratorRepository;

    private UserRepository<User, Object> userRepository;

    private SessionIdGeneratorRepository<IdGenerator<String>> sessionIdGeneratorRepository;

    private SessionFactoryRepository<SessionFactory<Session, SessionProperties>> sessionFactoryRepository;

    private SessionRepository<Session> sessionRepository;

    private UserLoginStateRepository<UserLoginState, Object> userLoginStateRepository;

    private UserLoginStateFactoryRepository<UserLoginStateFactory<UserLoginState, UserLoginStateProperties>> userLoginStateFactoryRepository;

    public ThirdPartyloginProcesses(
            UuidUserBindFactoryRepository<?> uuidUserBindFactoryRepository,
            UuidUserBindRepository<?> uuidUserBindRepository,
            UserFactoryRepository<?> userFactoryRepository,
            UserIdGeneratorRepository<?> userIdGeneratorRepository,
            UserRepository<?, ?> userRepository,
            SessionIdGeneratorRepository<?> sessionIdGeneratorRepository,
            SessionFactoryRepository<?> sessionFactoryRepository,
            SessionRepository<?> sessionRepository,
            UserLoginStateRepository<?, ?> userLoginStateRepository,
            UserLoginStateFactoryRepository<?> userLoginStateFactoryRepository) {
        this.uuidUserBindFactoryRepository = (UuidUserBindFactoryRepository<UuidUserBindFactory<UuidUserBind, UuidUserBindProperties>>) uuidUserBindFactoryRepository;
        this.uuidUserBindRepository = (UuidUserBindRepository<UuidUserBind>) uuidUserBindRepository;
        this.userFactoryRepository = (UserFactoryRepository<UserFactory<User, UserProperties>>) userFactoryRepository;
        this.userIdGeneratorRepository = (UserIdGeneratorRepository<IdGenerator<Object>>) userIdGeneratorRepository;
        this.userRepository = (UserRepository<User, Object>) userRepository;
        this.sessionIdGeneratorRepository = (SessionIdGeneratorRepository<IdGenerator<String>>) sessionIdGeneratorRepository;
        this.sessionFactoryRepository = (SessionFactoryRepository<SessionFactory<Session, SessionProperties>>) sessionFactoryRepository;
        this.sessionRepository = (SessionRepository<Session>) sessionRepository;
        this.userLoginStateRepository = (UserLoginStateRepository<UserLoginState, Object>) userLoginStateRepository;
        this.userLoginStateFactoryRepository = (UserLoginStateFactoryRepository<UserLoginStateFactory<UserLoginState, UserLoginStateProperties>>) userLoginStateFactoryRepository;
    }

    public <USR extends User> LoginOrRegisterResult<USR> loginOrRegister(
            String uuid, UuidUserBindProperties uuidUserBindProperties,
            UserProperties userProperties, SessionProperties sessionProperties,
            UserLoginStateProperties userLoginStateProperties) {
        LoginOrRegisterResult<USR> rslt = new LoginOrRegisterResult<>();
        UuidUserBind uuidUserBind = uuidUserBindRepository.find(uuid);
        Object userId;
        if (uuidUserBind == null) {
            UuidUserBindFactory<UuidUserBind, UuidUserBindProperties> uuidUserBindFactory = uuidUserBindFactoryRepository.get();
            UuidUserBind newBind = uuidUserBindFactory
                    .create(uuidUserBindProperties);
            newBind.setId(uuid);
            UuidUserBind existsBind = uuidUserBindRepository.putIfAbsent(newBind);
            if (existsBind == null) {// register
                IdGenerator<Object> userIdGenerator = userIdGeneratorRepository.take();
                UserFactory<User, UserProperties> userFactory = userFactoryRepository.get();
                User userToRegister = userFactory.create(userProperties);
                userToRegister.setId(userIdGenerator.generateId());
                userRepository.put(userToRegister);
                userId = userToRegister.getId();
                newBind.setUserId(userId);
                rslt.setUser((USR) userToRegister);
                rslt.setCreateNewUser(true);
            } else {
                userId = existsBind.getUserId();
                rslt.setUser((USR) userRepository.find(userId));
            }
        } else {
            userId = uuidUserBind.getUserId();
            rslt.setUser((USR) userRepository.find(userId));
        }

        IdGenerator<String> sessionIdGenerator = sessionIdGeneratorRepository.take();
        SessionFactory<Session, SessionProperties> sessionFactory = sessionFactoryRepository.get();
        Session newSession = sessionFactory.createSession(sessionProperties);
        newSession.setId(sessionIdGenerator.generateId());
        newSession.setUserId(userId);
        sessionRepository.put(newSession);
        UserLoginState userLoginState = userLoginStateRepository.take(userId);
        if (userLoginState != null) {
            Session existsSession = sessionRepository
                    .remove(userLoginState.getSessionId());
            rslt.setFinishedSession(existsSession);
            userLoginState.setSessionId(newSession.getId());
        } else {
            UserLoginStateFactory<UserLoginState, UserLoginStateProperties> userLoginStateFactory = userLoginStateFactoryRepository.get();
            userLoginState = userLoginStateFactory.create(userLoginStateProperties);
            userLoginState.setId(userId);
            userLoginState.setSessionId(newSession.getId());
            userLoginStateRepository.put(userLoginState);
        }
        rslt.setNewSession(newSession);
        return rslt;
    }
}
